package com.smile.service.uitl;


import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;

/**
 * 坐标转换工具类
 * <p>
 * 参考文章-实现的Java版本：https://github.com/wandergis/coordtransform
 */
@Slf4j
public class CoordinateTransformUtil {


    static double x_pi = 3.14159265358979324 * 3000.0 / 180.0;
    // π
    static double pi = 3.1415926535897932384626;
    // 长半轴
    static double a = 6378245.0;
    // 扁率
    static double ee = 0.00669342162296594323;

    /**
     * WGS84 转 GCJ-02
     *
     * @param lng WGS84经度
     * @param lat WGS84纬度
     * @return
     */
    public static Coordinate wgs84ToGcj02(BigDecimal lng,BigDecimal  lat) {
        if (outOfChina(lng, lat)) {
            return new Coordinate(lng, lat);
        }
        double dlat = transformlat(lng.doubleValue() - 105.0, lat.doubleValue() - 35.0);
        double dlng = transformlng(lng.doubleValue() - 105.0, lat.doubleValue() - 35.0);
        double radlat = lat.doubleValue() / 180.0 * pi;
        double magic = Math.sin(radlat);
        magic = 1 - ee * magic * magic;
        double sqrtmagic = Math.sqrt(magic);
        dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * pi);
        dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * pi);
        double mgLat = lat.doubleValue() + dlat;
        double mgLng = lng.doubleValue() + dlng;
        return new Coordinate(BigDecimal.valueOf(mgLng), BigDecimal.valueOf(mgLat));
    }

    /**
     * GCJ-02 转 WGS84
     *
     * @param lng GCJ-02经度
     * @param lat GCJ-02纬度
     * @return
     */
    public static Coordinate gcj02ToWgs84(BigDecimal lng, BigDecimal lat) {
        if (outOfChina(lng, lat)) {
            return new Coordinate(lng, lat);
        }
        double dlat = transformlat(lng.doubleValue() - 105.0, lat.doubleValue() - 35.0);
        double dlng = transformlng(lng.doubleValue() - 105.0, lat.doubleValue() - 35.0);
        double radlat = lat.doubleValue() / 180.0 * pi;
        double magic = Math.sin(radlat);
        magic = 1 - ee * magic * magic;
        double sqrtmagic = Math.sqrt(magic);
        dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * pi);
        dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * pi);
        double mgLat = lat.doubleValue() + dlat;
        double mgLng = lng.doubleValue() + dlng;
        return new Coordinate(BigDecimal.valueOf(lng.doubleValue() * 2 - mgLng), BigDecimal.valueOf(lat.doubleValue() * 2 - mgLat));
    }

    /**
     * GCJ-02 转 BD-09
     *
     * @param lng GCJ-02经度
     * @param lat GCJ-02纬度
     * @return
     */
    public static Coordinate gcj02ToBd09(BigDecimal lng, BigDecimal lat) {
        double z = Math.sqrt(lng.doubleValue() * lng.doubleValue() + lat.doubleValue() * lat.doubleValue()) + 0.00002 * Math.sin(lat.doubleValue() * x_pi);
        double theta = Math.atan2(lat.doubleValue(), lng.doubleValue()) + 0.000003 * Math.cos(lng.doubleValue() * x_pi);
        double bd_lng = z * Math.cos(theta) + 0.0065;
        double bd_lat = z * Math.sin(theta) + 0.006;
        return new Coordinate(BigDecimal.valueOf(bd_lng), BigDecimal.valueOf(bd_lat));
    }


    /**
     * BD-09 转 GCJ-02
     *
     * @param lng BD-09经度
     * @param lat BD-09纬度
     */
    public static Coordinate bd09ToGcj02(BigDecimal lng, BigDecimal lat) {
        double x = lng.doubleValue() - 0.0065;
        double y = lat.doubleValue() - 0.006;
        double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
        double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
        double gg_lng = z * Math.cos(theta);
        double gg_lat = z * Math.sin(theta);
        return new Coordinate(BigDecimal.valueOf(gg_lng), BigDecimal.valueOf(gg_lat));
    }

    /**
     * BD-09 转 WGS84
     *
     * @param lng BD-09经度
     * @param lat BD-09纬度
     * @return
     */
    public static Coordinate bd09ToWgs84(BigDecimal lng, BigDecimal lat) {
        Coordinate gcj02 = bd09ToGcj02(lng, lat);
        Coordinate wgs84 = gcj02ToWgs84(gcj02.longitude, gcj02.latitude);
        return wgs84;
    }


    /**
     * WGS84 转 BD-09
     *
     * @param lng WGS84经度
     * @param lat WGS84纬度
     * @return
     */
    public static Coordinate wgs84ToBd09(BigDecimal lng, BigDecimal lat) {
        Coordinate gcj02 = wgs84ToGcj02(lng, lat);
        Coordinate bd09 = gcj02ToBd09(gcj02.longitude, gcj02.latitude);
        return bd09;
    }


    /**
     * 纬度转换
     *
     * @param lng
     * @param lat
     * @return
     */
    public static double transformlat(double lng, double lat) {
        double ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
        ret += (20.0 * Math.sin(6.0 * lng * pi) + 20.0 * Math.sin(2.0 * lng * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(lat * pi) + 40.0 * Math.sin(lat / 3.0 * pi)) * 2.0 / 3.0;
        ret += (160.0 * Math.sin(lat / 12.0 * pi) + 320 * Math.sin(lat * pi / 30.0)) * 2.0 / 3.0;
        return ret;
    }

    /**
     * 经度转换
     *
     * @param lng
     * @param lat
     * @return
     */
    public static double transformlng(double lng, double lat) {
        double ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
        ret += (20.0 * Math.sin(6.0 * lng * pi) + 20.0 * Math.sin(2.0 * lng * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(lng * pi) + 40.0 * Math.sin(lng / 3.0 * pi)) * 2.0 / 3.0;
        ret += (150.0 * Math.sin(lng / 12.0 * pi) + 300.0 * Math.sin(lng / 30.0 * pi)) * 2.0 / 3.0;
        return ret;
    }

    /**
     * 判断是否在国内，不在国内不做偏移
     *
     * @param lng
     * @param lat
     * @return
     */
    public static boolean outOfChina(BigDecimal lng, BigDecimal lat) {
        if (lng.doubleValue() < 72.004 || lng.doubleValue() > 137.8347) {
            return true;
        } else if (lat.doubleValue() < 0.8293 || lat.doubleValue() > 55.8271) {
            return true;
        }
        return false;
    }

    /**
     * 坐标类
     */
    @Data
    public static class Coordinate {
        /**
         * 经度
         */
        private BigDecimal longitude;

        /**
         * 维度
         */
        private BigDecimal latitude;

        public Coordinate(BigDecimal lonBigDecimal, BigDecimal latBigDecimal) {
            // 保留6位小数，四舍五入模式
//            BigDecimal latBigDecimal = new BigDecimal(latitude).setScale(6, RoundingMode.HALF_UP);
//            BigDecimal lonBigDecimal = new BigDecimal(longitude).setScale(6, RoundingMode.HALF_UP);
            this.longitude = lonBigDecimal;
            this.latitude = latBigDecimal;
//            this.longitude = longitude;
//            this.latitude = latitude;
        }
    }

    // 定义CGCS2000的坐标系
    private final static String CGCS2000 = "EPSG:4490";
    // 定义WGS84的坐标系
    final static String WGS84 = "EPSG:4326";

//    /**
//     * CGCS2000 转 WGS84
//     *
//     * @param lng CGCS2000经度
//     * @param lat CGCS2000纬度
//     * @return
//     */
//    public static Coordinate cgcs2000ToWgs84(double lng, double lat) {
//
//        CRS crsFactory = new CRSFactory();
//        // 创建CGCS2000的坐标参考系统
//        CoordinateReferenceSystem sourceCRS = crsFactory.createFromName(CGCS2000);
//        // 创建WGS84的坐标参考系统
//        CoordinateReferenceSystem targetCRS = crsFactory.createFromName(WGS84);
//
//        // 定义坐标转换器
//        CoordinateTransformFactory ctFactory = new CoordinateTransformFactory();
//        // 创建转换器
//        CoordinateTransform transform = ctFactory.createTransform(sourceCRS, targetCRS);
//        // 执行坐标转换
//        ProjCoordinate srcCoord = new ProjCoordinate(lng, lat);
//        ProjCoordinate targetCoord = new ProjCoordinate();
//        transform.transform(srcCoord, targetCoord);
//        // 4. 输出转换后的正常经纬度坐标
//        return new Coordinate(targetCoord.x, targetCoord.y);
//    }
//
//    /**
//     * WGS84 转 CGCS2000
//     *
//     * @param lng WGS84经度
//     * @param lat WGS84纬度
//     * @return
//     */
//    public static Coordinate wgs84ToCgcs2000(double lng, double lat) {
//        CRSFactory crsFactory = new CRSFactory();
//        // 定义源和目标投影
//        CoordinateReferenceSystem sourceCRS = crsFactory.createFromName(WGS84);
//        CoordinateReferenceSystem targetCRS = crsFactory.createFromName(CGCS2000);
//
//        // 定义坐标转换器
//        CoordinateTransformFactory ctFactory = new CoordinateTransformFactory();
//        // 创建转换器
//        CoordinateTransform transform = ctFactory.createTransform(sourceCRS, targetCRS);
//        // 执行坐标转换
//        ProjCoordinate srcCoord = new ProjCoordinate(lng, lat);
//        ProjCoordinate targetCoord = new ProjCoordinate();
//        transform.transform(srcCoord, targetCoord);
//        // 输出转换后的正常经纬度坐标
//        return new Coordinate(targetCoord.x, targetCoord.y);
//    }

    public static void main(String[] args) {
        double GPSLon = 119.90206989082;
        double GPSLat = 30.24983900163;
        Coordinate wgs84ToGcj02 = CoordinateTransformUtil.wgs84ToGcj02(BigDecimal.valueOf(GPSLon), BigDecimal.valueOf(GPSLat));
        Coordinate wgs84ToBd09 = CoordinateTransformUtil.wgs84ToBd09(BigDecimal.valueOf(GPSLon), BigDecimal.valueOf(GPSLat));
        log.info("GPS wgs84ToGcj02 : longitude={}, latitude={}", wgs84ToGcj02.longitude, wgs84ToGcj02.latitude);
        log.info("GPS wgs84ToBd09  : longitude={}, latitude={}", wgs84ToBd09.longitude, wgs84ToBd09.latitude);

        double aMapLon2 = 119.90662357377992;
        double aMapLat2 = 30.24719383920937;
        Coordinate gcj02ToWgs84 = CoordinateTransformUtil.gcj02ToWgs84(BigDecimal.valueOf(aMapLon2), BigDecimal.valueOf(aMapLat2));
        Coordinate gcj02ToBd09 = CoordinateTransformUtil.gcj02ToBd09(BigDecimal.valueOf(aMapLon2), BigDecimal.valueOf(aMapLat2));
        log.info("高德 gcj02ToWgs84 : longitude={}, latitude={}", gcj02ToWgs84.longitude, gcj02ToWgs84.latitude);
        log.info("高德 gcj02ToBd09  : longitude={}, latitude={}", gcj02ToBd09.longitude, gcj02ToBd09.latitude);

        double baiduLon3 = 108.887314;
        double baiduLat3 = 34.230897;
        Coordinate bd09ToWgs84 = CoordinateTransformUtil.bd09ToWgs84(BigDecimal.valueOf(baiduLon3), BigDecimal.valueOf(baiduLat3));
        Coordinate bd09ToGcj02 = CoordinateTransformUtil.bd09ToGcj02(BigDecimal.valueOf(baiduLon3), BigDecimal.valueOf(baiduLat3));
        log.info("百度 gcj02ToWgs84 : longitude={}, latitude={}", bd09ToWgs84.longitude, bd09ToWgs84.latitude);
        log.info("百度 gcj02ToBd09  : longitude={}, latitude={}", bd09ToGcj02.longitude, bd09ToGcj02.latitude);

        /**
         * CGCS2000
         */
        double CGCS2000Lon4 = 108.887314;
        double CGCS2000Lat4 = 34.230897;
//        CoordinateTransformUtil.Coordinate CGCS2000ToWgs84 = CoordinateTransformUtil.cgcs2000ToWgs84(CGCS2000Lon4, CGCS2000Lat4);
//        CoordinateTransformUtil.Coordinate CGCS2000ToBd09 = CoordinateTransformUtil.wgs84ToCgcs2000(CGCS2000Lon4, CGCS2000Lat4);
//        log.info("中国2000 CGCS2000ToWgs84 : longitude={}, latitude={}", CGCS2000ToWgs84.longitude, CGCS2000ToWgs84.latitude);
//        log.info("中国2000 CGCS2000ToBd09  : longitude={}, latitude={}", CGCS2000ToBd09.longitude, CGCS2000ToBd09.latitude);
    }

}

